/**
 * Name: Joshua Bailey
 * Date: January 30, 2008
 * Course and Section: CSCE 146 Sec 001
 * Description: This is a class that represents a five-card poker hand.  It consists of an array of 5 card objects.
 * 				It also contains methods that determine the "rank" of the hand.
 */

public class PokerHand  {

    private Card[] hand;
    private Card[] sortedHand;  //this holds a sorted version of the hand, which makes it easier to determine the rank
    private int rank;


    /**
     * Constructor that takes in 5 Card objects to create a hand
     * Input: 5 Card objects
     */
    public PokerHand(Card c1, Card c2, Card c3, Card c4, Card c5)  {
	hand = new Card[5];  //create an array of 5 cards
	hand[0] = c1;
	hand[1] = c2;
	hand[2] = c3;	
	hand[3] = c4;
	hand[4] = c5;	
	createSortedHand(); //create the sorted version of the hand

	rank = 0;

	if(this.containsPair())
	    rank = 1;

	if(this.containsTwoPair())
	    rank = 2;

	if(this.containsThreeOfAKind())
	    rank = 3;

	if(this.containsStraight())
	    rank = 4;

	if(this.containsFlush())
	    rank = 5;

	if(this.containsFullHouse())
	    rank = 6;
	   
	if(this.containsFourOfAKind())
	    rank = 7;

	if(this.containsStraightFlush())
	    rank = 8;

	if(this.containsRoyalFlush())
	    rank = 9;

    }


    /**
     * getHand()
     * Input: none
     * Output: A reference to the hand array
     * A method that returns a reference to the hand array
     */
    public Card[] getHand()  {
	return hand;
    }

    /**
     * getRank()
     * Input: none
     * Output: an integer representing the rank of the hand
     * A method that returns the rank of a hand
     */
    public int getRank()  {
	return rank;
    }

    /**
     * toString()
     * Input: none
     * Output: A string representation of this hand
     */
    public String toString()  {
	return "" + rank;
    }

    /**
     * createSortedHand()
     * Input: none
     * Output: none
     * This private method creates the sortedHand array, that is useful
     * in determining the rank of the hand
     */
    private void createSortedHand()  {
	sortedHand = new Card[5];  //create an array of 5 cards

	//copy the hand array into the sorted hand array
	for(int i = 0; i < hand.length; i++)
	    sortedHand[i] = hand[i];

	//sort the sortedhand array using selection sort
	int minIndex;
	Card temp;
	for(int i = 0; i < sortedHand.length; i++)  {
	    minIndex = i;
	    for(int j = i+1; j < sortedHand.length; j++)  {
		if(sortedHand[minIndex].isHigher(sortedHand[j]))  {
		    minIndex = j;
		}
	    }
	    temp = sortedHand[minIndex];
	    sortedHand[minIndex] = sortedHand[i];
	    sortedHand[i] = temp;
	}
    }//end createSortedHand()



    /**
     * containsPair()
     * Input: none
     * Output: boolean -- true if the hand contains a pair, false otherwise
     * This method checks to see if there is two cards with the same number in the hand.
     */
    public boolean containsPair()  {
    	//for loop that goes through each card in the hand
    	for(int i = 0; i < sortedHand.length -1; i++)  {
    		//checks to see if there are two cards with the same number
    		if(sortedHand[i].getRank() == sortedHand[i+1].getRank())  {
    			return true;
    		}
    	}
	return false;
    }

    /**
     * containsTwoPair()
     * Input: none
     * Output: boolean -- true if the hand contains two pair, false otherwise
     * This method checks to see if there are two different pairs in the hand
     */
    public boolean containsTwoPair()  {
    	//Counter that counts the number of pairs
    	int count = 0;
    	//for loop that goes through each card in the hand
    	for(int i = 0; i < sortedHand.length -1; i++)  {
    		//if a pair is found then add one to the counter
    		if(sortedHand[i].getRank() == sortedHand[i+1].getRank())  {
    			count++;
    		}
    	}
    	//if the counter is equal to 2 then there are two pairs in the hand
    	if(count == 2)  {
    		return true;
    	}
	return false;
    }

    /**
     * containsThreeOfAKind()
     * Input: none
     * Output: boolean -- true if the hand contains three of a kind, false otherwise
     * This method checks to see if there are three cards in the hand with the same number
     */
    public boolean containsThreeOfAKind()  {
    	//goes through each card
    	for(int i = 0; i < sortedHand.length -2; i++)  {
    		//if there are three cards with the same number(rank) return true
    		if(sortedHand[i].getRank() == sortedHand[i+1].getRank() && sortedHand[i+1].getRank() == sortedHand[i+2].getRank())  {
    			return true;
    		}
    	}
	return false;
    }

    /**
     * containsStraight()
     * Input: none
     * Output: boolean -- true if the hand contains a straight, false otherwise
     * This method checks to see if the cards in the hand are consecutive
     */
    public boolean containsStraight()  {
    	//if cards are consecutive then return true
    	if( (sortedHand[0].getRank()+1 == sortedHand[1].getRank() && sortedHand[1].getRank()+1 == sortedHand[2].getRank() && 
    			sortedHand[3].getRank()+1 == sortedHand[4].getRank()) || (sortedHand[0].getRank()+1 == sortedHand[1].getRank() && 
    			sortedHand[1].getRank()+1 == sortedHand[2].getRank() && sortedHand[0].getRank()-1 == sortedHand[4].getRank()%13))  {
    		return true;
    	}
	return false;
    }

    /**
     * containsFlush()
     * Input: none
     * Output: boolean -- true if the hand contains a flush
     * This method checks to see if all the cards in the hand are of the same suit
     */
    public boolean containsFlush()  {
    	//counter that counts the number of cards with the same suit
    	int counter = 0;
    	//goes through each card
    	for(int i = 0; i < sortedHand.length -1; i++)  {
    		//if the suits are the same then add one to the counter
    		if(sortedHand[i].getSuit() == sortedHand[i+1].getSuit())  {
    			counter++;
    		}
    	}
    	//if the counter is equal to four then the hand contains a flush
    	if(counter == 4)  {
    		return true;
    	}
	return false;
    }

    /**
     * Input: none
     * Output: boolean -- true if the hand contains a full house
     * This methods checks to see if the hand contains a pair and a three of a kind 
     */
    public boolean containsFullHouse()  {
    	/**
    	 * checks to see if first three cards have same number and last two cards have the same number or the first two cards have the same 
    	 * number and the last three have the same number
    	 */
    	if( (sortedHand[0].getRank() == sortedHand[1].getRank() && sortedHand[1].getRank() == sortedHand[2].getRank() && 
    			sortedHand[2].getRank() != sortedHand[3].getRank() && sortedHand[3].getRank() == sortedHand[4].getRank()) || 
    			sortedHand[0].getRank() == sortedHand[1].getRank() && sortedHand[1].getRank() != sortedHand[2].getRank() && 
    			sortedHand[2].getRank() == sortedHand[3].getRank() && sortedHand[3].getRank() == sortedHand[4].getRank())  {
    		return true;
    	}
	return false;
    }

    /**
     * Input: none
     * Output: boolean -- true if the hand contains Four of a Kind
     * This method checks to see if there are four cards with the same number
    */
    public boolean containsFourOfAKind()  {
    	//goes through each card and if four cards have the same number then return true
    	for(int i = 0; i < sortedHand.length -3; i++)  {
    		if(sortedHand[i].getRank() == sortedHand[i+1].getRank() && sortedHand[i+1].getRank() == sortedHand[i+2].getRank() && 
    				sortedHand[i+2].getRank() == sortedHand[i+3].getRank())  {
    			return true;
    		}
    	}
	return false;
    }    

    /**
     * Input: none
     * Output: boolean -- true if the hand contains a straight flush
     * This method checks to see if the hand contains a straight and all cards have the same suit
     */
    public boolean containsStraightFlush()  {
    	//if the hand contains a flush and the hand contains a straight then return true
    	if(this.containsFlush() && this.containsStraight())
    	{
    		return true;
    	}
	return false;
    }

    /**
     * Input: none
     * Output: boolean -- true if the hand contains a royal flush
     * This method test to see if the hand is a straight flush starting with 10 and ending with an Ace
     */
    public boolean containsRoyalFlush()  {
    	int mySuit = sortedHand[0].getSuit();
    	if(sortedHand[0].getRank() == 10 && sortedHand[0].getSuit() == mySuit && sortedHand[1].getRank() == 11 && sortedHand[1].getSuit() == mySuit && 
    			sortedHand[2].getRank() == 12 && sortedHand[2].getSuit() == mySuit && sortedHand[3].getRank() == 13 && sortedHand[3].getSuit() == mySuit
    			&& sortedHand[4].getRank() == 14 && sortedHand[4].getSuit() == mySuit)  {
    		return true;
    	}
	return false;
    }

}